1 module hip.api.net.controller; 2 public import hip.api.net.hipnet; 3 4 /** 5 Example usage 6 ```d 7 struct Action 8 { 9 ubyte fromX, fromY, toX, toY; 10 } 11 12 struct BoardState 13 { 14 Action[] actions; 15 } 16 struct AssignColor 17 { 18 black, 19 white 20 } 21 22 alias ChessNetController = NetController!(MarkNetData!( 23 Action, 24 AssignColor, 25 BoardState 26 )); 27 28 ChessNetController c = new ChessNetController(getNetworkInterface); 29 c.connect(NetIPAddress("127.0.0.1", 10_000)); 30 c.on_connect(() 31 { 32 c.sendData([BoardState()]); 33 }); 34 c.on_disconnect(() 35 { 36 logg("Waiting for other player to connect..."); 37 }); 38 39 with(c.poll) 40 { 41 switch(typeID) 42 { 43 case Types.disconnect: //Disconnect event has no data 44 break; 45 case Types.Action: 46 Action act = getAction(); 47 break; 48 case Types.AssignColor: 49 AssignColor c = getAssignColor(); 50 break; 51 default: //Received an invalid typeID. 52 break; 53 } 54 } 55 ``` 56 */ 57 class NetController(alias NetData) 58 { 59 import std.traits:isInstanceOf; 60 import hip.api.net.server; 61 import hip.util.reflection; 62 static assert(isInstanceOf!(MarkNetData, NetData), "NetController only accepts NetData as an input."); 63 64 struct NetControllerResult 65 { 66 NetData.idType typeID; 67 ubyte[] data; 68 69 static alias Types = NetData.Types; 70 71 ///Reserved for the destructor freeing buffer 72 private NetBuffer* buffer; 73 ///Reserved for the destructor freeing buffer. 74 private INetwork net; 75 76 static foreach(t; NetData.RegisteredTypes) 77 { 78 mixin("t get",t.stringof,"(){return interpretNetworkData!(t)(buffer.header, data);}"); 79 } 80 81 ~this() 82 { 83 if(net !is null && buffer !is null) 84 { 85 net.freeBuffer(buffer); 86 net = null; 87 buffer = null; 88 data = null; 89 typeID = 0; 90 } 91 } 92 } 93 94 INetwork network; 95 protected void delegate() connectFn; 96 protected void delegate() disconnectFn; 97 98 static foreach(m; __traits(allMembers, MarkedNetReservedTypes)) 99 { 100 static if(Attributes!(MarkedNetReservedTypes, m).length) 101 { 102 static if(is(Attributes!(MarkedNetReservedTypes, m)[0].Response == void)) 103 { 104 mixin("protected void delegate() on_",m,"_fn;\n", 105 "void on_",m,"(void delegate() v){ on_",m,"_fn = v;}" 106 ); 107 } 108 else 109 { 110 mixin("protected void delegate(Attributes!(MarkedNetReservedTypes, m)[0].Response) on_",m,"_fn;\n", 111 "void on_",m,"(void delegate(Attributes!(MarkedNetReservedTypes, m)[0].Response) v){ on_",m,"_fn = v;}" 112 ); 113 } 114 } 115 } 116 117 static foreach(t; NetData.RegisteredTypes) 118 { 119 mixin("protected void delegate(t) ", t.stringof, "Handler;"); 120 void registerHandler(void delegate(t) handler) 121 { 122 mixin(t.stringof, "Handler = handler;"); 123 } 124 125 /** 126 * Send the data. It can only send data via this NetController if it is part of one of the 127 * registered types from the MarkNetData 128 * 129 * Params: 130 * data = Data that will be sent with a type information 131 */ 132 void sendData(t data) 133 { 134 NetData.sendData(network, data); 135 } 136 } 137 138 /** 139 * Use `targetConnectionID` if you wish to change the ID after connection. 140 * 141 * Params: 142 * ip = The IPAddress to connect 143 * id = The ID within the IP 144 * Returns: Status. Almost always will be pending 145 */ 146 NetConnectStatus connect(NetIPAddress ip, uint id = NetID.server) 147 { 148 return network.connect(ip, (INetwork net) 149 { 150 net.send_connect(); 151 // getConnectedClients(); 152 }, id); 153 } 154 155 156 void getConnectedClients() 157 { 158 network.send_get_connected_clients(); 159 } 160 161 pragma(inline, true) 162 final uint getConnectionSelfID() const{return network.getConnectionSelfID();} 163 164 pragma(inline, true) 165 final void targetConnectionID(uint id){network.targetConnectionID(id);} 166 167 pragma(inline, true) 168 final uint targetConnectionID() const {return network.targetConnectionID();} 169 170 pragma(inline, true) 171 final bool isHost() const { return network.isHost; } 172 173 this(INetwork network){this.network = network;} 174 175 /** 176 * Polls the result from the socket/network data. 177 * 178 * WARNING: For getting correct data type, you MUST send the data using either NetData.sendData or 179 * NetController.sendData 180 * 181 * If you send through the socket, no type info will be sent together. 182 * 183 * Returns: A result that includes the typeID, sliced data and managed net buffer. 184 */ 185 NetControllerResult poll() 186 { 187 if(network.getData()) 188 { 189 NetBuffer* buffer = network.getCompletedBuffer(); 190 ubyte[] data = buffer.getFinishedBuffer(); 191 NetData.idType typeID = NetData.getDataFromBuffer(data); 192 193 switch(typeID) with(NetControllerResult.Types) 194 { 195 static foreach(i, t; NetData.RegisteredTypes) 196 { 197 case mixin(t): 198 mixin("if(",t,"Handler !is null) ", t,"Handler(interpretNetworkData!(t)(buffer.header, data));"); 199 goto default; 200 } 201 static foreach(m; __traits(allMembers, MarkedNetReservedTypes)) 202 { 203 static if(Attributes!(MarkedNetReservedTypes, m).length) 204 { 205 case mixin(m): 206 alias fn = mixin("on_",m,"_fn"); 207 static if(is(Attributes!(MarkedNetReservedTypes, m)[0].Response == void)) 208 { 209 if(fn) fn(); 210 } 211 else 212 { 213 if(fn) fn(interpretNetworkData!(Parameters!(fn)[0])(buffer.header, data)); 214 } 215 goto default; 216 } 217 } 218 default: 219 break; 220 } 221 return NetControllerResult(typeID, data, buffer, network); 222 } 223 224 return NetControllerResult(NetData.Types.invalid); 225 } 226 } 227 228 T getNetworkArray(T, E = typeof(T.init[0]))(ubyte[] data) 229 { 230 import hip.api.net.utils; 231 assert(data.length >= 4, "Data received is not an actual array since it is smaller than a length element."); 232 uint length = *(cast(uint*)data.ptr); 233 static if(hasDynamicArray!E) //Dynamic arrays for type of dynamic size 234 { 235 T temp; 236 temp.length = length; 237 size_t offset = uint.sizeof; 238 foreach(i; 0..length) 239 { 240 temp[i] = getNetworkStruct!(E)(data[offset..$]); 241 offset+= getSendTypeSize(temp[i]); 242 } 243 return temp; 244 } 245 else //Static size 246 { 247 assert(data.length >= 4 + length * E.sizeof, "Data received is not valid.Not enough space available for type "~E.stringof); 248 return (cast(E*)(data.ptr + uint.sizeof))[0..length].dup; 249 } 250 } 251 252 253 T getNetworkStruct(T)(ubyte[] data) 254 { 255 import hip.api.net.utils; 256 import std.traits:isDynamicArray; 257 258 static if(hasDynamicArray!T) 259 { 260 T ret; 261 size_t offset; 262 foreach(ref v; ret.tupleof) 263 { 264 static if(isDynamicArray!(typeof(v))) 265 v = getNetworkArray!(typeof(v))(data[offset..$]); 266 else 267 v = getNetworkStruct!(typeof(v))(data[offset..$]); 268 offset+= getSendTypeSize(v); 269 } 270 return ret; 271 } 272 else 273 { 274 assert(data.length >= T.sizeof, "Data length is not enough to be interpreted as "~T.stringof); 275 return *cast(T*)data.ptr; 276 } 277 } 278 279 T interpretNetworkData(T)(NetHeader header, ubyte[] data) 280 { 281 import std.traits; 282 static if(is(T == string)) 283 { 284 if(header.type != NetDataType.text) 285 throw new Exception("Unmatched data type when trying to get a string."); 286 return cast(string)data; 287 } 288 else static if(is(T == struct)) 289 { 290 return getNetworkStruct!T(data); 291 } 292 else 293 { 294 if(header.type != NetDataType.binary) 295 throw new Exception("Unmatched data type when trying to get a binary."); 296 297 static if(isArray!T) 298 { 299 size_t arraySize = header.length / T.init[0].sizeof; 300 static if(isStaticArray!T) 301 { 302 if(arraySize != T.init.length) 303 throw new Exception("Received more data than the static array size of "~T.init.length.stringof); 304 return (cast(T)data)[0..T.init.length]; 305 } 306 else 307 { 308 return cast(T)data; 309 } 310 } 311 else 312 { 313 return *cast(T*)data.ptr; 314 } 315 } 316 }